﻿namespace Fiddler
{
    using Microsoft.Win32;
    using System;
    using System.Collections;
    using System.Collections.Specialized;
    using System.Configuration;
    using System.Diagnostics;
    using System.IO;
    using System.Net;
    using System.Net.NetworkInformation;
    using System.Net.Sockets;
    using System.Security.Cryptography.X509Certificates;
    using System.Text;
    using System.Threading;
    using System.Windows.Forms;

    public class Proxy : IDisposable
    {
        private bool _bDetaching;
        private bool _bIsAttached;
        internal IPEndPoint _DefaultEgressEndPoint;
        private IPEndPoint _ipepFtpGateway;
        private IPEndPoint _ipepHttpGateway;
        private IPEndPoint _ipepHttpsGateway;
        private X509Certificate2 _oHTTPSCertificate;
        private string _sHTTPSHostname;
        private EventHandler _DetachedUnexpectedly;
        internal static PipePool htServerPipePool = new PipePool();
        private Socket oAcceptor;
        internal WinINETConnectoids oAllConnectoids;
        internal WinHTTPAutoProxy oAutoProxy;
        private ProxyBypassList oBypassList;
        internal RegistryWatcher oRegistryWatcher;
        internal WinINETProxyInfo piPrior;
        private PreferenceBag.PrefWatcher? watcherPrefNotify = new PreferenceBag.PrefWatcher?();

        public event EventHandler DetachedUnexpectedly
        {
            add
            {
                EventHandler handler2;
                EventHandler detachedUnexpectedly = this._DetachedUnexpectedly;
                do
                {
                    handler2 = detachedUnexpectedly;
                    EventHandler handler3 = (EventHandler) Delegate.Combine(handler2, value);
                    detachedUnexpectedly = Interlocked.CompareExchange<EventHandler>(ref this._DetachedUnexpectedly, handler3, handler2);
                }
                while (detachedUnexpectedly != handler2);
            }
            remove
            {
                EventHandler handler2;
                EventHandler detachedUnexpectedly = this._DetachedUnexpectedly;
                do
                {
                    handler2 = detachedUnexpectedly;
                    EventHandler handler3 = (EventHandler) Delegate.Remove(handler2, value);
                    detachedUnexpectedly = Interlocked.CompareExchange<EventHandler>(ref this._DetachedUnexpectedly, handler3, handler2);
                }
                while (detachedUnexpectedly != handler2);
            }
        }

        internal Proxy(bool bIsPrimary)
        {
            if (bIsPrimary)
            {
                try
                {
                    NetworkChange.NetworkAvailabilityChanged += new NetworkAvailabilityChangedEventHandler(this.NetworkChange_NetworkAvailabilityChanged);
                    NetworkChange.NetworkAddressChanged += new NetworkAddressChangedEventHandler(this.NetworkChange_NetworkAddressChanged);
                }
                catch (Exception)
                {
                }
                try
                {
                    this.watcherPrefNotify = new PreferenceBag.PrefWatcher?(FiddlerApplication.Prefs.AddWatcher("fiddler.network", new EventHandler<PrefChangeEventArgs>(this.onNetworkPrefsChange)));
                    this.SetDefaultEgressEndPoint(FiddlerApplication.Prefs["fiddler.network.egress.ip"]);
                    CONFIG.SetNoDecryptList(FiddlerApplication.Prefs["fiddler.network.https.NoDecryptionHosts"]);
                    CONFIG.sFiddlerListenHostPort = FiddlerApplication.Prefs.GetStringPref("fiddler.network.proxy.RegistrationHostName", "127.0.0.1") + ":" + CONFIG.ListenPort.ToString();
                    ClientChatter._cbClientReadBuffer = FiddlerApplication.Prefs.GetInt32Pref("fiddler.network.sockets.ClientReadBufferSize", 0x2000);
                    ServerChatter._cbServerReadBuffer = FiddlerApplication.Prefs.GetInt32Pref("fiddler.network.sockets.ServerReadBufferSize", 0x8000);
                }
                catch (Exception)
                {
                }
            }
        }

        private void _DetermineGatewayIPEndPoints()
        {
            this._ipepHttpGateway = GetFirstRespondingEndpoint(this.piPrior.sHttpProxy);
            if (this.piPrior.sHttpsProxy == this.piPrior.sHttpProxy)
            {
                this._ipepHttpsGateway = this._ipepHttpGateway;
            }
            else
            {
                this._ipepHttpsGateway = GetFirstRespondingEndpoint(this.piPrior.sHttpsProxy);
            }
            if (this.piPrior.sFtpProxy == this.piPrior.sHttpProxy)
            {
                this._ipepFtpGateway = this._ipepHttpGateway;
            }
            else
            {
                this._ipepFtpGateway = GetFirstRespondingEndpoint(this.piPrior.sFtpProxy);
            }
        }

        internal string _GetPACScriptText(bool bUseFiddler)
        {
            string stringPref;
            if (bUseFiddler)
            {
                stringPref = FiddlerApplication.Prefs.GetStringPref("fiddler.proxy.pacfile.text", "return 'PROXY " + CONFIG.sFiddlerListenHostPort + "';");
            }
            else
            {
                stringPref = "return 'DIRECT';";
            }
            return ("//Autogenerated file; do not edit. Rewritten on attach and detach of Fiddler.\n//This Automatic Proxy Configuration script can be used by non-WinINET browsers.\n//Point your browser's Proxy AutoConfiguration URL to:\n// file:///" + Utilities.UrlPathEncode(CONFIG.GetPath("Pac")) + "\n\nfunction FindProxyForURL(url, host){\n  " + stringPref + "\n}");
        }

        private static void _setDynamicRegistryKey(bool bAttached)
        {
            if (!CONFIG.bIsViewOnly)
            {
                try
                {
                    RegistryKey key = Registry.CurrentUser.CreateSubKey(CONFIG.GetRegPath("Dynamic"));
                    if (key != null)
                    {
                        key.SetValue("Attached", bAttached ? 1 : 0, RegistryValueKind.DWord);
                        key.Close();
                    }
                }
                catch (Exception exception)
                {
                    FiddlerApplication.Log.LogFormat("fiddler.network.FiddlerHook> Unable to set Dynamic registry key; registry permissions likely corrupt. Exception: {0}", new object[] { exception.Message });
                }
            }
        }

        private void AcceptConnection(IAsyncResult ar)
        {
            try
            {
                ProxyExecuteParams state = new ProxyExecuteParams(this.oAcceptor.EndAccept(ar), this._oHTTPSCertificate);
                ThreadPool.UnsafeQueueUserWorkItem(new WaitCallback(Session.CreateAndExecute), state);
            }
            catch (ObjectDisposedException)
            {
                return;
            }
            catch (Exception)
            {
            }
            try
            {
                this.oAcceptor.BeginAccept(new AsyncCallback(this.AcceptConnection), null);
            }
            catch (Exception)
            {
            }
        }

        internal bool ActAsHTTPSEndpointForHostname(string sHTTPSHostname)
        {
            try
            {
                if (string.IsNullOrEmpty(sHTTPSHostname))
                {
                    throw new ArgumentException();
                }
                this._oHTTPSCertificate = CertMaker.FindCert(sHTTPSHostname);
                this._sHTTPSHostname = this._oHTTPSCertificate.Subject;
                return true;
            }
            catch (Exception)
            {
                this._oHTTPSCertificate = null;
                this._sHTTPSHostname = null;
            }
            return false;
        }

        internal void AssignEndpointCertificate(X509Certificate2 certHTTPS)
        {
            this._oHTTPSCertificate = certHTTPS;
            if (certHTTPS != null)
            {
                this._sHTTPSHostname = certHTTPS.Subject;
            }
            else
            {
                this._sHTTPSHostname = null;
            }
        }

        public bool Attach()
        {
            return this.Attach(false);
        }

        internal bool Attach(bool bCollectGWInfo)
        {
            if (!this._bIsAttached)
            {
                if (bCollectGWInfo)
                {
                    this.CollectConnectoidAndGatewayInfo();
                }
                WinINETProxyInfo oNewInfo = new WinINETProxyInfo();
                oNewInfo.bUseManualProxies = true;
                oNewInfo.bAllowDirect = true;
                oNewInfo.sHttpProxy = CONFIG.sFiddlerListenHostPort;
                if (CONFIG.bCaptureCONNECT)
                {
                    oNewInfo.sHttpsProxy = CONFIG.sFiddlerListenHostPort;
                }
                else if ((this.piPrior != null) && this.piPrior.bUseManualProxies)
                {
                    oNewInfo.sHttpsProxy = this.piPrior.sHttpsProxy;
                }
                if (CONFIG.bCaptureFTP)
                {
                    oNewInfo.sFtpProxy = CONFIG.sFiddlerListenHostPort;
                }
                else if ((this.piPrior != null) && this.piPrior.bUseManualProxies)
                {
                    oNewInfo.sFtpProxy = this.piPrior.sFtpProxy;
                }
                if ((this.piPrior != null) && this.piPrior.bUseManualProxies)
                {
                    oNewInfo.sSocksProxy = this.piPrior.sSocksProxy;
                }
                oNewInfo.sHostsThatBypass = CONFIG.sHostsThatBypassFiddler;
                if (CONFIG.bHookWithPAC)
                {
                    if (FiddlerApplication.Prefs.GetBoolPref("fiddler.proxy.pacfile.usefileprotocol", true))
                    {
                        oNewInfo.sPACScriptLocation = "file://" + CONFIG.GetPath("Pac");
                    }
                    else
                    {
                        oNewInfo.sPACScriptLocation = "http://" + CONFIG.sFiddlerListenHostPort + "/proxy.pac";
                    }
                }
                if (!CONFIG.bIsViewOnly)
                {
                    if (this.oAllConnectoids.HookConnections(oNewInfo))
                    {
                        this._bIsAttached = true;
                        FiddlerApplication.OnFiddlerAttach();
                        this.WriteAutoProxyPACFile(true);
                        if ((this.oRegistryWatcher == null) && FiddlerApplication.Prefs.GetBoolPref("fiddler.proxy.WatchRegistry", true))
                        {
                            this.oRegistryWatcher = RegistryWatcher.WatchKey(RegistryHive.CurrentUser, @"Software\Microsoft\Windows\CurrentVersion\Internet Settings", new EventHandler(this.ProxyRegistryKeysChanged));
                        }
                    }
                    else
                    {
                        FiddlerApplication.DoNotifyUser("Failed to register Fiddler as the system proxy.", "Error");
                        _setDynamicRegistryKey(false);
                        return false;
                    }
                    _setDynamicRegistryKey(true);
                }
            }
            return true;
        }

        internal void CollectConnectoidAndGatewayInfo()
        {
            this.oAllConnectoids = new WinINETConnectoids();
            this.piPrior = this.oAllConnectoids.GetDefaultConnectionGatewayInfo();
            if (CONFIG.bForwardToGateway && ((this.piPrior.sPACScriptLocation != null) || this.piPrior.bAutoDetect))
            {
                this.oAutoProxy = new WinHTTPAutoProxy(this.piPrior.bAutoDetect, this.piPrior.sPACScriptLocation);
            }
            else if (this.oAutoProxy != null)
            {
                this.oAutoProxy.Dispose();
                this.oAutoProxy = null;
            }
            if (CONFIG.bForwardToGateway && this.piPrior.bUseManualProxies)
            {
                this._DetermineGatewayIPEndPoints();
                if (string.IsNullOrEmpty(this.piPrior.sHostsThatBypass))
                {
                    this.oBypassList = null;
                }
                else
                {
                    this.oBypassList = new ProxyBypassList(this.piPrior.sHostsThatBypass);
                    if (!this.oBypassList.HasEntries)
                    {
                        this.oBypassList = null;
                    }
                }
            }
            else
            {
                this._ipepFtpGateway = this._ipepHttpGateway = (IPEndPoint) (this._ipepHttpsGateway = null);
                this.oBypassList = null;
            }
        }

        public bool Detach()
        {
            return this.Detach(false);
        }

        internal bool Detach(bool bDontCheckIfAttached)
        {
            if (bDontCheckIfAttached || this._bIsAttached)
            {
                if (CONFIG.bIsViewOnly)
                {
                    return true;
                }
                try
                {
                    this._bDetaching = true;
                    _setDynamicRegistryKey(false);
                    if (this.oAllConnectoids.UnhookAllConnections())
                    {
                        this._bIsAttached = false;
                        FiddlerApplication.OnFiddlerDetach();
                        this.WriteAutoProxyPACFile(false);
                    }
                    else
                    {
                        return false;
                    }
                }
                finally
                {
                    this._bDetaching = false;
                }
            }
            return true;
        }

        public void Dispose()
        {
            if (this.watcherPrefNotify.HasValue)
            {
                FiddlerApplication.Prefs.RemoveWatcher(this.watcherPrefNotify.Value);
            }
            if (this.oAutoProxy != null)
            {
                this.oAutoProxy.Dispose();
                this.oAutoProxy = null;
            }
            if (this.oRegistryWatcher != null)
            {
                this.oRegistryWatcher.Dispose();
            }
            this.Stop();
        }

        public IPEndPoint FindGatewayForOrigin(string sURIScheme, string sHostAndPort)
        {
            if (!Utilities.isLocalhost(sHostAndPort))
            {
                if (sURIScheme.Equals("http", StringComparison.OrdinalIgnoreCase))
                {
                    if (sHostAndPort.EndsWith(":80", StringComparison.Ordinal))
                    {
                        sHostAndPort = sHostAndPort.Substring(0, sHostAndPort.Length - 3);
                    }
                }
                else if (sURIScheme.Equals("https", StringComparison.OrdinalIgnoreCase))
                {
                    if (sHostAndPort.EndsWith(":443", StringComparison.Ordinal))
                    {
                        sHostAndPort = sHostAndPort.Substring(0, sHostAndPort.Length - 4);
                    }
                }
                else if (sURIScheme.Equals("ftp", StringComparison.OrdinalIgnoreCase) && sHostAndPort.EndsWith(":21", StringComparison.Ordinal))
                {
                    sHostAndPort = sHostAndPort.Substring(0, sHostAndPort.Length - 3);
                }
                if ((this.oAutoProxy != null) && (this.oAutoProxy.iAutoProxySuccessCount > -1))
                {
                    IPEndPoint point;
                    if (this.oAutoProxy.GetAutoProxyForUrl(sURIScheme + "://" + sHostAndPort, out point))
                    {
                        this.oAutoProxy.iAutoProxySuccessCount = 1;
                        return point;
                    }
                    if ((this.oAutoProxy.iAutoProxySuccessCount == 0) && !FiddlerApplication.Prefs.GetBoolPref("fiddler.network.gateway.UseFailedAutoProxy", false))
                    {
                        FiddlerApplication.Log.LogString("AutoProxy failed. Disabling for this network.");
                        this.oAutoProxy.iAutoProxySuccessCount = -1;
                    }
                }
                if (((this.piPrior == null) || !this.piPrior.bBypassIntranetHosts) || !Utilities.isPlainHostName(sHostAndPort))
                {
                    if ((this.oBypassList != null) && this.oBypassList.IsBypass(sURIScheme + "://" + sHostAndPort))
                    {
                        return null;
                    }
                    if (sURIScheme.Equals("http", StringComparison.OrdinalIgnoreCase))
                    {
                        return this._ipepHttpGateway;
                    }
                    if (sURIScheme.Equals("https", StringComparison.OrdinalIgnoreCase))
                    {
                        return this._ipepHttpsGateway;
                    }
                    if (sURIScheme.Equals("ftp", StringComparison.OrdinalIgnoreCase))
                    {
                        return this._ipepFtpGateway;
                    }
                }
            }
            return null;
        }

        private static IPEndPoint GetFirstRespondingEndpoint(string sHostPortList)
        {
            string str;
            IPAddress[] addressArray;
            if (Utilities.IsNullOrWhiteSpace(sHostPortList))
            {
                return null;
            }
            sHostPortList = Utilities.TrimAfter(sHostPortList, ';');
            IPEndPoint point = null;
            int iPort = 80;
            Utilities.CrackHostAndPort(sHostPortList, out str, ref iPort);
            try
            {
                addressArray = DNSResolver.GetIPAddressList(str, true, null);
            }
            catch
            {
                FiddlerApplication.Log.LogFormat("fiddler.network.gateway> Unable to resolve upstream proxy '{0}'... ignoring.", new object[] { sHostPortList });
                return null;
            }
            try
            {
                foreach (IPAddress address in addressArray)
                {
                    try
                    {
                        using (Socket socket = new Socket(address.AddressFamily, SocketType.Stream, ProtocolType.Tcp))
                        {
                            socket.NoDelay = true;
                            if (FiddlerApplication.oProxy._DefaultEgressEndPoint != null)
                            {
                                socket.Bind(FiddlerApplication.oProxy._DefaultEgressEndPoint);
                            }
                            socket.Connect(address, iPort);
                            point = new IPEndPoint(address, iPort);
                        }
                        break;
                    }
                    catch (Exception exception)
                    {
                        if (!FiddlerApplication.Prefs.GetBoolPref("fiddler.network.dns.fallback", true))
                        {
                            break;
                        }
                        FiddlerApplication.Log.LogFormat("fiddler.network.gateway.connect>Connection to {0} failed. {1}. Will try DNS Failover if available.", new object[] { address.ToString(), exception.Message });
                    }
                }
                return point;
            }
            catch (Exception)
            {
                return null;
            }
        }

        public void InjectCustomRequest(string sRequest)
        {
            if (this.oAcceptor == null)
            {
                this.InjectCustomRequest(sRequest, null);
            }
            else
            {
                Socket socket = new Socket(IPAddress.Loopback.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                socket.Connect(new IPEndPoint(IPAddress.Loopback, CONFIG.ListenPort));
                socket.Send(Encoding.UTF8.GetBytes(sRequest));
                socket.Shutdown(SocketShutdown.Both);
                socket.Close();
            }
        }

        public void InjectCustomRequest(string sRequest, StringDictionary oNewFlags)
        {
            this.SendRequest(sRequest, oNewFlags);
        }

        public void InjectCustomRequest(HTTPRequestHeaders oHeaders, byte[] arrRequestBodyBytes, StringDictionary oNewFlags)
        {
            this.SendRequest(oHeaders, arrRequestBodyBytes, oNewFlags);
        }

        [Obsolete("This overload of InjectCustomRequest is obsolete. Use a different version.", true)]
        public void InjectCustomRequest(HTTPRequestHeaders oHeaders, byte[] arrRequestBodyBytes, bool bRunRequestRules, bool bViewResult)
        {
            StringDictionary oNewFlags = new StringDictionary();
            oNewFlags["x-From-Builder"] = "true";
            if (bViewResult)
            {
                oNewFlags["x-Builder-Inspect"] = "1";
            }
            this.InjectCustomRequest(oHeaders, arrRequestBodyBytes, oNewFlags);
        }

        private void NetworkChange_NetworkAddressChanged(object sender, EventArgs e)
        {
            try
            {
                DNSResolver.ClearCache();
                FiddlerApplication.Log.LogString("NetworkAddressChanged.");
                if (this.oAutoProxy != null)
                {
                    this.oAutoProxy.iAutoProxySuccessCount = 0;
                }
                if ((CONFIG.bForwardToGateway && (this.piPrior != null)) && this.piPrior.bUseManualProxies)
                {
                    this._DetermineGatewayIPEndPoints();
                }
            }
            catch (Exception exception)
            {
                FiddlerApplication.ReportException(exception);
            }
        }

        private void NetworkChange_NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
        {
            try
            {
                this.PurgeServerPipePool();
                FiddlerApplication.Log.LogFormat("fiddler.network.availability.change> Network Available: {0}", new object[] { e.IsAvailable });
                FiddlerApplication.UI.sbpInfo.Text = e.IsAvailable ? "Windows reports that Network Connectivity was restored." : "Windows reports that Network Connectivity was lost.";
                if (FiddlerToolbar.IsShowing())
                {
                    FiddlerToolbar.UpdateNetworkStatus();
                }
            }
            catch (Exception)
            {
            }
        }

        protected virtual void OnDetachedUnexpectedly()
        {
            EventHandler detachedUnexpectedly = this._DetachedUnexpectedly;
            if (detachedUnexpectedly != null)
            {
                detachedUnexpectedly(this, null);
            }
        }

        private void onNetworkPrefsChange(object sender, PrefChangeEventArgs oPCE)
        {
            if (oPCE.PrefName.StartsWith("fiddler.network.timeouts."))
            {
                if (oPCE.PrefName == "fiddler.network.timeouts.serverpipe.send.initial")
                {
                    ServerPipe._timeoutSendInitial = FiddlerApplication.Prefs.GetInt32Pref("fiddler.network.timeouts.serverpipe.send.initial", -1);
                    return;
                }
                if (oPCE.PrefName == "fiddler.network.timeouts.serverpipe.send.reuse")
                {
                    ServerPipe._timeoutSendReused = FiddlerApplication.Prefs.GetInt32Pref("fiddler.network.timeouts.serverpipe.send.reuse", -1);
                    return;
                }
                if (oPCE.PrefName == "fiddler.network.timeouts.serverpipe.receive.initial")
                {
                    ServerPipe._timeoutReceiveInitial = FiddlerApplication.Prefs.GetInt32Pref("fiddler.network.timeouts.serverpipe.receive.initial", -1);
                    return;
                }
                if (oPCE.PrefName == "fiddler.network.timeouts.serverpipe.receive.reuse")
                {
                    ServerPipe._timeoutReceiveReused = FiddlerApplication.Prefs.GetInt32Pref("fiddler.network.timeouts.serverpipe.receive.reuse", -1);
                    return;
                }
                if (oPCE.PrefName == "fiddler.network.timeouts.serverpipe.reuse")
                {
                    PipePool.MSEC_PIPE_POOLED_LIFETIME = (uint) FiddlerApplication.Prefs.GetInt32Pref("fiddler.network.timeouts.serverpipe.reuse", 0x1d4c0);
                    return;
                }
                if (oPCE.PrefName == "fiddler.network.timeouts.clientpipe.receive.initial")
                {
                    ClientPipe._timeoutReceiveInitial = FiddlerApplication.Prefs.GetInt32Pref("fiddler.network.timeouts.clientpipe.receive.initial", 0xea60);
                    return;
                }
                if (oPCE.PrefName == "fiddler.network.timeouts.clientpipe.receive.reuse")
                {
                    ClientPipe._timeoutReceiveReused = FiddlerApplication.Prefs.GetInt32Pref("fiddler.network.timeouts.clientpipe.receive.reuse", 0x7530);
                    return;
                }
                if (oPCE.PrefName == "fiddler.network.timeouts.dnscache")
                {
                    DNSResolver.MSEC_DNS_CACHE_LIFETIME = (ulong) FiddlerApplication.Prefs.GetInt32Pref("fiddler.network.timeouts.dnscache", 0x249f0);
                    return;
                }
            }
            if (oPCE.PrefName == "fiddler.network.sockets.ClientReadBufferSize")
            {
                ClientChatter._cbClientReadBuffer = FiddlerApplication.Prefs.GetInt32Pref("fiddler.network.sockets.ClientReadBufferSize", 0x2000);
            }
            else if (oPCE.PrefName == "fiddler.network.sockets.ServerReadBufferSize")
            {
                ServerChatter._cbServerReadBuffer = FiddlerApplication.Prefs.GetInt32Pref("fiddler.network.sockets.ServerReadBufferSize", 0x8000);
            }
            else if (oPCE.PrefName == "fiddler.network.egress.ip")
            {
                this.SetDefaultEgressEndPoint(oPCE.ValueString);
            }
            else if (oPCE.PrefName == "fiddler.network.https.NoDecryptionHosts")
            {
                CONFIG.SetNoDecryptList(oPCE.ValueString);
            }
            else if (oPCE.PrefName == "fiddler.network.proxy.RegistrationHostName")
            {
                CONFIG.sFiddlerListenHostPort = FiddlerApplication.Prefs.GetStringPref("fiddler.network.proxy.RegistrationHostName", "127.0.0.1") + ":" + CONFIG.ListenPort.ToString();
            }
        }

        private void ProxyRegistryKeysChanged(object sender, EventArgs e)
        {
            if ((this._bIsAttached && !this._bDetaching) && FiddlerApplication.Prefs.GetBoolPref("fiddler.proxy.WatchRegistry", true))
            {
                ScheduledTasks.ScheduleWork("VerifyAttached", 50, new SimpleEventHandler(this.VerifyAttached));
            }
        }

        public void PurgeServerPipePool()
        {
            htServerPipePool.Clear();
        }

        public Session SendRequest(string sRequest, StringDictionary oNewFlags)
        {
            int num;
            int num2;
            HTTPHeaderParseWarnings warnings;
            byte[] buffer2;
            byte[] bytes = CONFIG.oHeaderEncoding.GetBytes(sRequest);
            if (!Parser.FindEntityBodyOffsetFromArray(bytes, out num, out num2, out warnings))
            {
                throw new ArgumentException("sRequest did not represent a valid HTTP request", "sRequest");
            }
            string sHeaders = CONFIG.oHeaderEncoding.GetString(bytes, 0, num) + "\r\n\r\n";
            HTTPRequestHeaders oHeaders = new HTTPRequestHeaders();
            if (!oHeaders.AssignFromString(sHeaders))
            {
                throw new ArgumentException("sRequest did not contain valid HTTP headers", "sRequest");
            }
            if (1 > (bytes.Length - num2))
            {
                buffer2 = new byte[0];
            }
            else
            {
                buffer2 = new byte[bytes.Length - num2];
                Buffer.BlockCopy(bytes, num2, buffer2, 0, buffer2.Length);
            }
            return this.SendRequest(oHeaders, buffer2, oNewFlags);
        }

        public Session SendRequest(HTTPRequestHeaders oHeaders, byte[] arrRequestBodyBytes, StringDictionary oNewFlags)
        {
            if (oHeaders.ExistsAndContains("Fiddler-Encoding", "base64"))
            {
                oHeaders.Remove("Fiddler-Encoding");
                if (!Utilities.IsNullOrEmpty(arrRequestBodyBytes))
                {
                    arrRequestBodyBytes = Convert.FromBase64String(Encoding.ASCII.GetString(arrRequestBodyBytes));
                    if (oNewFlags == null)
                    {
                        oNewFlags = new StringDictionary();
                    }
                    oNewFlags["x-Builder-FixContentLength"] = "CFE-required";
                }
            }
            if (oHeaders.Exists("Fiddler-Host"))
            {
                if (oNewFlags == null)
                {
                    oNewFlags = new StringDictionary();
                }
                oNewFlags["x-OverrideHost"] = oHeaders["Fiddler-Host"];
                oNewFlags["X-IgnoreCertCNMismatch"] = "Overrode HOST";
                if (!oNewFlags.ContainsKey("X-OverrideGateway"))
                {
                    oNewFlags["x-OverrideGateway"] = "DIRECT";
                }
                oHeaders.Remove("Fiddler-Host");
            }
            if ((oNewFlags != null) && oNewFlags.ContainsKey("x-Builder-FixContentLength"))
            {
                if ((arrRequestBodyBytes != null) && !oHeaders.ExistsAndContains("Transfer-Encoding", "chunked"))
                {
                    if (!Utilities.HTTPMethodAllowsBody(oHeaders.HTTPMethod) && (arrRequestBodyBytes.Length == 0))
                    {
                        oHeaders.Remove("Content-Length");
                    }
                    else
                    {
                        oHeaders["Content-Length"] = arrRequestBodyBytes.LongLength.ToString();
                    }
                }
                else
                {
                    oHeaders.Remove("Content-Length");
                }
            }
            Session session = new Session((HTTPRequestHeaders) oHeaders.Clone(), arrRequestBodyBytes);
            session.SetBitFlag(SessionFlags.RequestGeneratedByFiddler, true);
            if ((oNewFlags != null) && (oNewFlags.Count > 0))
            {
                foreach (DictionaryEntry entry in oNewFlags)
                {
                    session.oFlags[(string) entry.Key] = oNewFlags[(string) entry.Key];
                }
            }
            ThreadPool.UnsafeQueueUserWorkItem(new WaitCallback(session.Execute), null);
            return session;
        }

        private void SetDefaultEgressEndPoint(string sEgressIP)
        {
            if (string.IsNullOrEmpty(sEgressIP))
            {
                this._DefaultEgressEndPoint = null;
            }
            else
            {
                IPAddress address;
                if (IPAddress.TryParse(sEgressIP, out address))
                {
                    this._DefaultEgressEndPoint = new IPEndPoint(address, 0);
                }
                else
                {
                    this._DefaultEgressEndPoint = null;
                }
            }
        }

        internal bool Start(int iPort, bool bAllowRemote)
        {
            if (CONFIG.bIsViewOnly)
            {
                return false;
            }
            bool flag = false;
            try
            {
                flag = (bAllowRemote && CONFIG.bEnableIPv6) && Socket.OSSupportsIPv6;
            }
            catch (Exception exception)
            {
                if (exception is ConfigurationErrorsException)
                {
                    FiddlerApplication.DoNotifyUser(string.Concat(new object[] { "A Microsoft .NET configuration file (listed below) is corrupt and contains invalid data. You can often correct this error by installing updates from WindowsUpdate and/or reinstalling the .NET Framework.\n\n", exception.Message, "\nSource: ", exception.Source, "\n", exception.StackTrace, "\n\n", exception.InnerException, "\nFiddler v", Application.ProductVersion, (8 == IntPtr.Size) ? " (x64) " : " (x86) ", " [.NET ", Environment.Version, " on ", Environment.OSVersion.VersionString, "] " }), ".NET Configuration Error", MessageBoxIcon.Hand);
                    this.oAcceptor = null;
                    return false;
                }
            }
            try
            {
                if (flag)
                {
                    this.oAcceptor = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
                    if (Environment.OSVersion.Version.Major > 5)
                    {
                        this.oAcceptor.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.PacketInformation | SocketOptionName.KeepAlive, 0);
                    }
                }
                else
                {
                    this.oAcceptor = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                }
                if (CONFIG.ForceExclusivePort)
                {
                    this.oAcceptor.ExclusiveAddressUse = true;
                }
                if (bAllowRemote)
                {
                    if (flag)
                    {
                        this.oAcceptor.Bind(new IPEndPoint(IPAddress.IPv6Any, iPort));
                    }
                    else
                    {
                        this.oAcceptor.Bind(new IPEndPoint(IPAddress.Any, iPort));
                    }
                }
                else
                {
                    this.oAcceptor.Bind(new IPEndPoint(IPAddress.Loopback, iPort));
                }
                this.oAcceptor.Listen(50);
            }
            catch (SocketException exception2)
            {
                string str = string.Empty;
                string sTitle = "Fiddler Cannot Listen";
                switch (exception2.ErrorCode)
                {
                    case 0x273f:
                    case 0x2741:
                        if (flag)
                        {
                            str = "\nThis often means that you've enabled IPv6 support inside Tools > Fiddler Options, but your computer has IPv6 disabled.";
                        }
                        break;

                    case 0x2740:
                    case 0x271d:
                        str = "\nThis is usually due to another service running on this port. Run NETSTAT -A at a command prompt.\nIf you don't want to stop using the other program, simply change the port used by Fiddler.\nClick Tools > Fiddler Options > Connections, select a new port, and restart Fiddler.";
                        sTitle = "Fiddler Port in Use";
                        break;
                }
                this.oAcceptor = null;
                FiddlerApplication.DoNotifyUser(string.Format("Unable to bind to port [Localhost:{0}]. ErrorCode: {1}.\n{2}\n\n{3}\n\n{4}", new object[] { CONFIG.ListenPort, exception2.ErrorCode, str, exception2.ToString(), string.Concat(new object[] { "Fiddler v", Application.ProductVersion, " [.NET ", Environment.Version, " on ", Environment.OSVersion.VersionString, "]" }) }), sTitle, MessageBoxIcon.Hand);
                return false;
            }
            catch (UnauthorizedAccessException exception3)
            {
                this.oAcceptor = null;
                FiddlerApplication.ReportException(exception3, "Cannot Create Listener", "Fiddler could not start its Listener socket. This problem will occur if you attempt to\nrun Fiddler2 using the limited 'Guest' account. (Fiddler v4 does not have this limitation.)");
                return false;
            }
            catch (Exception exception4)
            {
                this.oAcceptor = null;
                FiddlerApplication.ReportException(exception4);
                return false;
            }
            try
            {
                this.oAcceptor.BeginAccept(new AsyncCallback(this.AcceptConnection), null);
            }
            catch (Exception exception5)
            {
                this.oAcceptor = null;
                FiddlerApplication.Log.LogFormat("Fiddler BeginAccept() Exception: {0}", new object[] { exception5.Message });
                return false;
            }
            return true;
        }

        internal void Stop()
        {
            if (this.oAcceptor != null)
            {
                try
                {
                    this.oAcceptor.LingerState = new LingerOption(true, 0);
                    this.oAcceptor.Close();
                }
                catch (Exception exception)
                {
                    Trace.WriteLine("oProxy.Dispose threw an exception: " + exception.Message);
                }
            }
        }

        public override string ToString()
        {
            return string.Format("Proxy instance is listening for requests on Port #{0}. HTTPS SubjectCN: {1}\n\n{2}", this.ListenPort, this._sHTTPSHostname ?? "<None>", htServerPipePool.InspectPool());
        }

        internal void VerifyAttached()
        {
            FiddlerApplication.Log.LogString("WinINET Registry change detected. Will verify proxy keys are intact.");
            bool flag = true;
            try
            {
                if (this.oAllConnectoids != null)
                {
                    flag = !this.oAllConnectoids.MarkUnhookedConnections(CONFIG.sFiddlerListenHostPort);
                }
            }
            catch (Exception)
            {
            }
            if (flag)
            {
                RegistryKey oReg = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Internet Settings", false);
                if (oReg != null)
                {
                    if (1 != Utilities.GetRegistryInt(oReg, "ProxyEnable", 0))
                    {
                        flag = false;
                    }
                    string str = oReg.GetValue("ProxyServer") as string;
                    if (str == null)
                    {
                        flag = false;
                    }
                    else if (!str.Equals(CONFIG.sFiddlerListenHostPort) && !str.Contains("http=" + CONFIG.sFiddlerListenHostPort))
                    {
                        flag = false;
                    }
                    oReg.Close();
                    if (!flag && (this.oAllConnectoids != null))
                    {
                        this.oAllConnectoids.MarkDefaultLANAsUnhooked();
                    }
                }
            }
            if (!flag)
            {
                this.OnDetachedUnexpectedly();
            }
        }

        private void WriteAutoProxyPACFile(bool bUseFiddler)
        {
            if (!CONFIG.bIsViewOnly)
            {
                try
                {
                    System.IO.File.WriteAllText(CONFIG.GetPath("Pac"), this._GetPACScriptText(bUseFiddler));
                }
                catch (Exception exception)
                {
                    FiddlerApplication.Log.LogFormat("Failed to update PAC file to indicate Listening={0} due to {1}", new object[] { bUseFiddler, exception.Message });
                }
            }
        }

        public bool IsAttached
        {
            get
            {
                return this._bIsAttached;
            }
            set
            {
                if (value)
                {
                    this.Attach();
                }
                else
                {
                    this.Detach();
                }
            }
        }

        public int ListenPort
        {
            get
            {
                if (this.oAcceptor != null)
                {
                    return (this.oAcceptor.LocalEndPoint as IPEndPoint).Port;
                }
                return 0;
            }
        }
    }
}

